// Package index contains types which are used to address Nix values in
// different data structures.
//
// These are designed so that there is a low risk of accidentally confusing
// indices into different structures.
package index

import (
	"math"
)

// Local represents the position of a future Nix value in the compile time
// locals list, with which stack slots are determined.
//
// This index is meaningless at runtime.
type Local int

// Stack represents the position of a Nix value in the stack at runtime.
type Stack int

// Capture represents the position of a Nix value in a thunk's capture list.
//
// Capture lists exist and correspond to each other at runtime, *and* at compile
// time, despite being indices into different lists.
type Capture int

// Position is used to represent from where to capture a local when setting up a
// thunk or closure.
//
// Position needs to be used with care as it represents different things at
// compile time (local indices) and runtime (stack indices).
type Position uint64

const Unknown Position = math.MaxUint64

func NewLocalPosition(idx Local) Position {
	return Position(idx << 1)
}

func NewStackPosition(idx Stack) Position {
	return Position(idx << 1)
}

func NewCapturePosition(idx Capture) Position {
	pos := Position(idx << 1)
	pos |= 1
	return pos
}

func (pos Position) CompileTimeLocalIndex() (Local, bool) {
	return Local(pos >> 1), pos&1 == 0
}

func (pos Position) RuntimeStackIndex() (Stack, bool) {
	return Stack(pos >> 1), pos&1 == 0
}

func (pos Position) CaptureIndex() (Capture, bool) {
	return Capture(pos >> 1), pos&1 == 1
}
